using System;
using System.Runtime.InteropServices;
using System.Net;
using System.Collections;
using System.Text;

namespace MooseNet
{
	/// <summary>
	/// Wrapper class for the Win32 IpHelper API.  This is only a partial 
	/// implementation of the IP Protocol Helper API.
	/// </summary>
	public class IPHelperAPI
	{
		/// <summary>
		/// Standard method return value for a success
		/// </summary>
		public const int NO_ERROR = 0;

		#region Public Structs and Enumerations

		/// <summary>
		/// TCPIP Statistics
		/// </summary>
		[StructLayout(LayoutKind.Sequential)]
		public struct MIB_TCPSTATS
		{
			/// <summary>
			/// Specifies the retransmission time-out (RTO) algorithm in use.
			/// </summary>
			public int dwRtoAlgorithm;
			/// <summary>
			/// Specifies the minimum retransmission time-out value in milliseconds.
			/// </summary>
			public int dwRtoMin;
			/// <summary>
			/// Specifies the maximum retransmission time-out value in milliseconds. 
			/// </summary>
			public int dwRtoMax;
			/// <summary>
			/// Specifies the maximum number of connections. If this member is -1, the maximum number of connections is variable. 
			/// </summary>
			public int dwMaxConn;
			/// <summary>
			/// Specifies the number of active opens. In an active open, the client is initiating a connection with the server.
			/// </summary>
			public int dwActiveOpens;
			/// <summary>
			/// Specifies the number of passive opens. In a passive open, the server is listening for a connection request from a client.
			/// </summary>
			public int dwPassiveOpens;
			/// <summary>
			/// Specifies the number of failed connection attempts.
			/// </summary>
			public int dwAttemptFails;
			/// <summary>
			/// Specifies the number of established connections that have been reset.
			/// </summary>
			public int dwEstabResets;
			/// <summary>
			/// Specifies the number of currently established connections.
			/// </summary>
			public int dwCurrEstab;
			/// <summary>
			/// Specifies the number of segments received. 
			/// </summary>
			public int dwInSegs;
			/// <summary>
			/// Specifies the number of segments transmitted. This number does not include retransmitted segments. 
			/// </summary>
			public int dwOutSegs;
			/// <summary>
			/// Specifies the number of segments retransmitted. 
			/// </summary>
			public int dwRetransSegs;
			/// <summary>
			/// Specifies the number of errors received. 
			/// </summary>
			public int dwInErrs;
			/// <summary>
			/// Specifies the number of segments transmitted with the reset flag set.
			/// </summary>
			public int dwOutRsts;
			/// <summary>
			/// Specifies the cumulative number of connections.
			/// </summary>
			public int dwNumConns;
		}

		/// <summary>
		/// TCP connection table
		/// </summary>
		public struct MIB_TCPTABLE 
		{
			/// <summary>
			/// Number of entries in the table.
			/// </summary>
			public int dwNumEntries;  
			/// <summary>
			/// Rows of the table.
			/// </summary>
			public MIB_TCPROW[] table;

		}

		/// <summary>
		/// TCP states
		/// </summary>
		public enum TcpState 
		{	
			/// <summary>
			/// Closed
			/// </summary>
			Closed = 1,
			/// <summary>
			/// Listening
			/// </summary>
			Listen = 2,
			/// <summary>
			/// Syn sent
			/// </summary>
			SynSent = 3,
			/// <summary>
			/// Syn received
			/// </summary>
			SynReceived = 4,
			/// <summary>
			/// Established
			/// </summary>
			Established = 5,
			/// <summary>
			/// FIN wait 1
			/// </summary>
			FinWait1 = 6, 
			/// <summary>
			/// FIN wait 2
			/// </summary>
			FinWait2 = 7, 
			/// <summary>
			/// Close wait
			/// </summary>
			CloseWait = 8,
			/// <summary>
			/// Closing
			/// </summary>
			Closing = 9,
			/// <summary>
			/// Last ack
			/// </summary>
			LastAck = 10,
			/// <summary>
			/// Time wait
			/// </summary>
			TimeWait = 11,
			/// <summary>
			/// Delete TCB
			/// </summary>
			DeleteTCB = 12 
		};

		/// <summary>
		/// Row in the TcpTable
		/// </summary>
		public struct MIB_TCPROW 
		{
			/// <summary>
			/// Specifies the state of the TCP connection.
			/// </summary>
			public TcpState state;
			/// <summary>
			/// Specifies the EndPoint for the connection on the local computer.
			/// </summary>
			public IPEndPoint local;  
			/// <summary>
			/// Specifies the EndPoint for the connection on the remote computer. 
			/// </summary>
			public IPEndPoint remote;
		}

		/// <summary>
		/// TCP connection table extended format
		/// </summary>
		public struct MIB_TCPTABLE_EX
		{
			/// <summary>
			/// Number of entries in the table
			/// </summary>
			public int dwNumEntries;
			/// <summary>
			/// Rows of the table
			/// </summary>
			public MIB_TCPROW_EX[] table;

		}

		/// <summary>
		/// Row of the extended TCP connection table
		/// </summary>
		public struct MIB_TCPROW_EX
		{
			/// <summary>
			/// Specifies the state of the TCP connection.
			/// </summary>
			public TcpState state;
			/// <summary>
			/// Specifies the EndPoint for the connection on the local computer.
			/// </summary>
			public IPEndPoint local;  
			/// <summary>
			/// Specifies the EndPoint for the connection on the remote computer. 
			/// </summary>
			public IPEndPoint remote;
			/// <summary>
			/// Id of the process that owns the connection
			/// </summary>
			public int dwProcessId;
		}

		/// <summary>
		/// UDP statistics
		/// </summary>
		[StructLayout(LayoutKind.Sequential)]
		public struct MIB_UDPSTATS
		{
			/// <summary>
			/// Specifies the number of datagrams received.
			/// </summary>
			public int dwInDatagrams;
			/// <summary>
			/// Specifies the number of received datagrams that were discarded because the port specified was invalid.
			/// </summary>
			public int dwNoPorts;
			/// <summary>
			/// Specifies the number of erroneous datagrams that were received. This number does not include the value contained by the dwNoPorts member.
			/// </summary>
			public int dwInErrors;
			/// <summary>
			/// Specifies the number of datagrams transmitted.
			/// </summary>
			public int dwOutDatagrams;
			/// <summary>
			/// Specifies the number of entries in the UDP listener table.
			/// </summary>
			public int dwNumAddrs;
		}

		/// <summary>
		/// Listening UDP end points table
		/// </summary>
		public struct MIB_UDPTABLE 
		{
			/// <summary>
			/// Number of entries in the table.
			/// </summary>
			public int dwNumEntries;  
			/// <summary>
			/// Rows of the table.
			/// </summary>
			public MIB_UDPROW[] table;
		}

		/// <summary>
		/// Row of the UDP table.
		/// </summary>
		public struct MIB_UDPROW 
		{
			/// <summary>
			/// Specifies the EndPoint for the connection on the local computer.
			/// </summary>
			public IPEndPoint local;
		}

		/// <summary>
		/// Extended listening UDP end points table
		/// </summary>
		public struct MIB_UDPTABLE_EX
		{
			/// <summary>
			/// Number of entries in the table.
			/// </summary>
			public int dwNumEntries;  
			/// <summary>
			/// Rows of the table.
			/// </summary>
			public MIB_UDPROW_EX[] table;
		}

		/// <summary>
		/// Row of the extended listening UDP end points table
		/// </summary>
		public struct MIB_UDPROW_EX
		{
			/// <summary>
			/// Specifies the EndPoint for the connection on the local computer.
			/// </summary>
			public IPEndPoint local;
			/// <summary>
			/// Id of the process that owns the connection
			/// </summary>
			public int dwProcessId;
		}

		/// <summary>
		/// Type of the network node
		/// </summary>
		public enum NodeType 
		{
			/// <summary>
			/// Unknown type
			/// </summary>
			Unknown = 0,
			/// <summary>
			/// Broadcast node type
			/// </summary>
			Broadcast = 1,
			/// <summary>
			/// Peer to peer node type
			/// </summary>
			PeerToPeer = 2,
			/// <summary>
			/// Mixed node type
			/// </summary>
			Mixed = 4,
			/// <summary>
			/// Hybrid node type
			/// </summary>
			Hybrid = 8
		}

		/// <summary>
		/// Data from GetNetworkParms()
		/// </summary>
		public struct FIXED_INFO
		{
			/// <summary>
			/// Host name for the local computer. 
			/// </summary>
			public string hostName;
			/// <summary>
			/// Domain in which the local computer is registered.
			/// </summary>
			public string domainName;
			/// <summary>
			/// ArrayList of IPAdress's for the DNS servers
			/// </summary>
			public ArrayList dnsServers;
			/// <summary>
			/// Node type of the local computer. 
			/// </summary>
			public NodeType type;
			/// <summary>
			/// DHCP scope name.
			/// </summary>
			public string scopeId;
			/// <summary>
			/// Specifies whether routing is enabled on the local computer. 
			/// </summary>
			public bool enableRouting;
			/// <summary>
			/// Specifies whether the local computer is acting as an ARP proxy.
			/// </summary>
			public bool enableProxy;
			/// <summary>
			/// Specifies whether DNS is enabled on the local computer.
			/// </summary>
			public bool enableDNS;
		}

		/// <summary>
		/// Adapter information
		/// </summary>
		public struct IP_ADAPTER_INFO 
		{
			/// <summary>
			/// Name of the adapter. 
			/// </summary>
			public string adapterName;
			/// <summary>
			/// Description for the adapter.
			/// </summary>
			public string description;  
			/// <summary>
			/// Hardware address
			/// </summary>
			public byte[] address;
			/// <summary>
			/// Adapter index.
			/// </summary>
			public int index;  
			/// <summary>
			/// Adapter type.
			/// </summary>
			public uint type;  
			/// <summary>
			/// Specifies whether dynamic host configuration protocol (DHCP) is enabled for this adapter.
			/// </summary>
			public bool dhcpEnabled; 
			/// <summary>
			/// List of IP addresses associated with this adapter. 
			/// </summary>
			public ArrayList ipAddressList;  
			/// <summary>
			/// IP address of the default gateway for this adapter.
			/// </summary>
			public ArrayList gatewayList;  
			/// <summary>
			/// IP address of the DHCP server for this adapter. 
			/// </summary>
			public ArrayList dhcpServerList;  
			/// <summary>
			/// Specifies whether this adapter uses Windows Internet Name Service (WINS). 
			/// </summary>
			public bool haveWins;  
			/// <summary>
			/// IP address of the primary WINS server. 
			/// </summary>
			public IPAddress primaryWinsServer; 
			/// <summary>
			/// IP address of the secondary WINS server. 
			/// </summary>
			public IPAddress secondaryWinsServer;  
			/// <summary>
			/// Time when the current DHCP lease was obtained. 
			/// </summary>
			public TimeSpan leaseObtained;  
			/// <summary>
			/// Time when the current DHCP lease expires.
			/// </summary>
			public TimeSpan leaseExpires;
		}

		#endregion

		/// <summary>
		/// The GetTcpStatistics function retrieves the TCP statistics for the local computer.
		/// </summary>
		/// <param name="stats">Reference to a MIB_TCPSTATS structure that receives the TCP statistics for the local computer.</param>
		/// <returns>If the function succeeds, the return value is NO_ERROR.</returns>
		[DllImport("iphlpapi.dll",SetLastError=true)]
		public extern static int GetTcpStatistics ( ref MIB_TCPSTATS stats );

		/// <summary>
		/// The GetUdpStatistics function retrieves the User Datagram Protocol (UDP) statistics for the local computer.
		/// </summary>
		/// <param name="pStats">Reference to a MIB_UDPSTATS structure that receives the UDP statistics for the local computer.</param>
		/// <returns>If the function succeeds, the return value is NO_ERROR.</returns>
		[DllImport("iphlpapi.dll",SetLastError=true)]
		public extern static int GetUdpStatistics ( ref MIB_UDPSTATS pStats );

		/// <summary>
		/// The GetTcpTable function retrieves the TCP connection table. 
		/// </summary>
		/// <param name="table">Reference to a MIB_TCPTABLE structure that receives the TCP connection table.</param>
		/// <returns>If the function succeeds, the return value is NO_ERROR.</returns>
		public static int GetTcpTable( ref MIB_TCPTABLE table )
		{
			byte[] buffer = null;
			int size = 0;
			int res = InternalGetTcpTable( buffer, ref size, true );
			if ( res != NO_ERROR )
			{
				buffer = new byte[size];
				res = InternalGetTcpTable( buffer, ref size, true );
				if ( res != NO_ERROR )
					return res;     
			}
			
			int offset = 0;
			table.dwNumEntries = Convert.ToInt32( buffer[offset] );
			offset += 4; 
			table.table = new MIB_TCPROW[table.dwNumEntries];

			for ( int i = 0; i < table.dwNumEntries; i++ )
			{
				// get the state
				int st = Convert.ToInt32( buffer[offset] );
				
				// convert the state to an enum
				table.table[i].state = ConvertTcpStateToEnum( st );
				offset += 4; 

				// local address
				string addr = buffer[offset].ToString() + "." + buffer[offset+1].ToString() + 
					"." + buffer[offset+2].ToString() + "." + buffer[offset+3].ToString();
				offset += 4; 

				//local port in decimal
				int port = (((int)buffer[offset]) << 8) + (((int)buffer[offset+1])) + 
					(((int)buffer[offset+2]) << 24) + (((int)buffer[offset+3]) << 16);

				offset += 4; 
				// store the remote endpoint
				table.table[i].local = new IPEndPoint( IPAddress.Parse(addr), port );

				// remote address
				addr = buffer[offset].ToString() + "." + buffer[offset+1].ToString() + 
					"." + buffer[offset+2].ToString() + "." + buffer[offset+3].ToString();
				offset += 4; 

				// if the remote address = 0 (0.0.0.0) the remote port is always 0
				// else get the remote port in decimal
				//
				if ( addr == "0.0.0.0" )
				{
					port = 0;
				}
				else
				{
					port = (((int)buffer[offset]) << 8) + (((int)buffer[offset+1])) + 
						(((int)buffer[offset+2]) << 24) + (((int)buffer[offset+3]) << 16);
				}
				offset += 4; 

				table.table[i].remote = new IPEndPoint( IPAddress.Parse(addr), port );
			}

			return NO_ERROR;
		}

		/// <summary>
		/// The GetTcpTableEx function retrieves an extended version the TCP connection table. 
		/// </summary>
		/// <param name="table">Reference to a MIB_TCPTABLE_EX structure that receives the TCP connection table.</param>
		/// <returns>If the function succeeds, the return value is NO_ERROR.</returns>
		public static int GetTcpTableEx( ref MIB_TCPTABLE_EX table )
		{
			int bufferSize = 100000;  // Use some ridiculously large buffer size

			IntPtr lpTable = Marshal.AllocHGlobal( bufferSize );

			//getting infos
			int res = InternalAllocateAndGetTcpExTableFromStack( ref lpTable, 
				true, 
				InternalGetProcessHeap(),
				0,
				2);

			if( res != NO_ERROR )
			{
				Marshal.FreeHGlobal( lpTable );
				lpTable = IntPtr.Zero;
				return res;
			}

			// Get the number of entries in the table
			int numEntries = (int)Marshal.ReadIntPtr( lpTable );

			// free allocated space in memory
			Marshal.FreeHGlobal(lpTable);
			lpTable = IntPtr.Zero;

			// Calculate the real buffer size 
			int rowsize = 24;
			bufferSize = ( numEntries * rowsize ) + 4;

			// Allocate memory
			lpTable = Marshal.AllocHGlobal( bufferSize );
			res = InternalAllocateAndGetTcpExTableFromStack( ref lpTable, 
				true,
				InternalGetProcessHeap(),
				0,
				2 );

			if ( res != NO_ERROR )
			{
				Marshal.FreeHGlobal( lpTable );
				lpTable = IntPtr.Zero;
				return res;
			}

			IntPtr current = lpTable;

			numEntries = (int)Marshal.ReadIntPtr(current);
			table.dwNumEntries = numEntries;

			// Make the array of entries
			table.table = new MIB_TCPROW_EX[numEntries];

			current = (IntPtr)((int)current + 4);

			// for each entries
			for ( int i = 0; i < numEntries; i++ )
			{
				table.table[i].state = ConvertTcpStateToEnum( (int)Marshal.ReadIntPtr(current) );
				current = (IntPtr)((int)current + 4);

				UInt32 addr = (UInt32)Marshal.ReadIntPtr( current );
				current = (IntPtr)((int)current + 4);

				UInt32 port = (UInt32)Marshal.ReadIntPtr( current );
				current = (IntPtr)((int)current + 4);

				table.table[i].local = new IPEndPoint( addr, (int)ConvertTcpPort( port ) );

				addr = (UInt32)Marshal.ReadIntPtr( current );
				current = (IntPtr)((int)current + 4);

				port = 0;
				// if the remote address = 0 (0.0.0.0) the remote port is always 0
				// else get the remote port
				if ( addr != 0 )
				{
					port = (UInt32)Marshal.ReadIntPtr( current );
					port = (UInt32)ConvertTcpPort( port );
				}
				current = (IntPtr)((int)current + 4);

				table.table[i].remote = new IPEndPoint( addr, (int)port );

				table.table[i].dwProcessId = (int)Marshal.ReadIntPtr( current );
				current = (IntPtr)((int)current + 4);
			}

			// free the buffer
			Marshal.FreeHGlobal( lpTable );
			current = IntPtr.Zero;

			return NO_ERROR;
		}

		/// <summary>
		/// The GetUdpTable function retrieves the UDP connection table. 
		/// </summary>
		/// <param name="table">Reference to a MIB_UDPTABLE structure that receives the UDP connection table.</param>
		/// <returns>If the function succeeds, the return value is NO_ERROR.</returns>
		public static int GetUdpTable( ref MIB_UDPTABLE table )
		{
			byte[] buffer = null;
			int size = 0;

			int res = InternalGetUdpTable( buffer, ref size, true );
			if ( res != NO_ERROR )
			{
				buffer = new byte[size];
				res = InternalGetUdpTable( buffer, ref size, true );
				if ( res != NO_ERROR )
					return res;     
			}

			int offset = 0;

			table.dwNumEntries = Convert.ToInt32( buffer[offset] );
			offset += 4; 

			table.table = new MIB_UDPROW[table.dwNumEntries];
			for ( int i = 0; i < table.dwNumEntries; i++ )
			{
				string addr = buffer[offset].ToString() + "." + buffer[offset+1].ToString() + 
					"." + buffer[offset+2].ToString() + "." + buffer[offset+3].ToString();
				offset += 4; 

				int port = (((int)buffer[offset]) << 8) + (((int)buffer[offset+1])) + 
					(((int)buffer[offset+2]) << 24) + (((int)buffer[offset+3]) << 16);
				offset += 4;
 
				table.table[i].local = new IPEndPoint( IPAddress.Parse(addr), port );
			}

			return NO_ERROR;
		}

		/// <summary>
		/// The GetUdpTableEx function retrieves an extended version the UDP connection table. 
		/// </summary>
		/// <param name="table">Reference to a MIB_UDPTABLE_EX structure that receives the UDP connection table.</param>
		/// <returns>If the function succeeds, the return value is NO_ERROR.</returns>
		public static int GetUdpTableEx( ref MIB_UDPTABLE_EX table )
		{
			int bufferSize = 100000;  // ridiculously large buffer
			IntPtr lpTable = Marshal.AllocHGlobal( bufferSize );

			int res = InternalAllocateAndGetUdpExTableFromStack( ref lpTable, 
				true, 
				InternalGetProcessHeap(),
				0,
				2);

			if ( res != NO_ERROR )
			{
				Marshal.FreeHGlobal( lpTable );
				lpTable = IntPtr.Zero;
				return res;
			}

			int numEntries = (int)Marshal.ReadIntPtr( lpTable );
			
			Marshal.FreeHGlobal( lpTable );
			lpTable = IntPtr.Zero;

			int rowsize = 12;
			bufferSize = ( numEntries * rowsize ) + 4;

			// Allocate memory
			lpTable = Marshal.AllocHGlobal( bufferSize );
			res = InternalAllocateAndGetUdpExTableFromStack( ref lpTable, 
				true,
				InternalGetProcessHeap(),
				0,
				2 );

			if ( res != NO_ERROR )
			{
				Marshal.FreeHGlobal( lpTable );
				lpTable = IntPtr.Zero;
				return res;
			}

			IntPtr current = lpTable;
			numEntries = (int)Marshal.ReadIntPtr( current );
			table.dwNumEntries = numEntries;

			table.table = new MIB_UDPROW_EX[numEntries];
			current = (IntPtr)((int)current + 4);

			// for each entries
			for ( int i = 0; i < numEntries; i++ )
			{
				UInt32 addr = (UInt32)Marshal.ReadIntPtr( current );
				current = (IntPtr)((int)current + 4);

				UInt32 port = (UInt32)Marshal.ReadIntPtr( current );
				current = (IntPtr)((int)current + 4);

				table.table[i].local = new IPEndPoint( addr, ConvertTcpPort( port ) );

				table.table[i].dwProcessId = (int)Marshal.ReadIntPtr( current );
				current = (IntPtr)((int)current + 4);
		
			}

			Marshal.FreeHGlobal(lpTable);
			current = IntPtr.Zero;
			return NO_ERROR;
		}

		/// <summary>
		/// The GetNetworkParams function retrieves network parameters for the local computer. 
		/// </summary>
		/// <param name="info">Reference to a FIXED_INFO structure that receives the network parameters for the local computer.</param>
		/// <returns>If the function succeeds, the return value is NO_ERROR.</returns>
		public static int GetNetworkParams( ref FIXED_INFO info )
		{
			IntPtr lpBuffer = IntPtr.Zero;
			int size = 0;
			int res = InternalGetNetworkParams( lpBuffer, ref size );
			if ( res != NO_ERROR )
			{
				lpBuffer = Marshal.AllocHGlobal( size );
				res = InternalGetNetworkParams( lpBuffer, ref size );
				if ( res != NO_ERROR )
				{
					Marshal.FreeHGlobal( lpBuffer );
					lpBuffer = IntPtr.Zero;
					return res;     
				}
			}

			IntPtr current = lpBuffer;
			
			info.hostName = Marshal.PtrToStringAnsi( current );
			current = (IntPtr)((int)current + 132);

			info.domainName = Marshal.PtrToStringAnsi( current );
			current = (IntPtr)((int)current + 132);
			
			// Ignore current DNS server
			current = (IntPtr)((int)current + 4);

			IntPtr list = current;
			info.dnsServers = new ArrayList();
			while ( list != IntPtr.Zero )
			{
				_IP_ADDR_STRING server = (_IP_ADDR_STRING)Marshal.PtrToStructure( list, typeof( _IP_ADDR_STRING ) );
				IPAddress ipaddr = IPAddress.Parse( server.ipAddress );
				info.dnsServers.Add( ipaddr );
				list = server.Next;
			}
			current = (IntPtr)((int)current + Marshal.SizeOf( typeof( _IP_ADDR_STRING ) ));

			info.type = (NodeType)Marshal.ReadInt32( current );
			current = (IntPtr)((int)current + 4);

			info.scopeId = Marshal.PtrToStringAnsi( current );
			current = (IntPtr)((int)current + 260);

			info.enableRouting = Marshal.ReadInt32( current ) == 1;
			current = (IntPtr)((int)current + 4);
			info.enableProxy = Marshal.ReadInt32( current ) == 1;
			current = (IntPtr)((int)current + 4);
			info.enableDNS = Marshal.ReadInt32( current ) == 1;

			Marshal.FreeHGlobal( lpBuffer );
			lpBuffer = IntPtr.Zero;

			return NO_ERROR;
		}

		/// <summary>
		/// The GetAdaptersInfo function retrieves adapter information for the local computer.
		/// </summary>
		/// <param name="infos">An ArrayList of IP_ADAPTER_INFO structures.</param>
		/// <returns></returns>
		public static int GetAdaptersInfo( ref ArrayList infos )
		{
			IntPtr lpBuffer = IntPtr.Zero;
			uint size = 0;

			infos = new ArrayList();

			int res = InternalGetAdaptersInfo( lpBuffer, ref size );
			if ( res != NO_ERROR )
			{
				lpBuffer = Marshal.AllocHGlobal( (int)size );
				res = InternalGetAdaptersInfo( lpBuffer, ref size );
				if ( res != NO_ERROR )
				{
					Marshal.FreeHGlobal( lpBuffer );
					lpBuffer = IntPtr.Zero;
					return res;     
				}
			}
			
			IntPtr pos = lpBuffer;

			while ( pos != IntPtr.Zero )
			{
				IP_ADAPTER_INFO info = new IP_ADAPTER_INFO();
				_IP_ADAPTER_INFO adapter = (_IP_ADAPTER_INFO)Marshal.PtrToStructure( pos, typeof( _IP_ADAPTER_INFO ) );

				info.adapterName = adapter.adapterName;
				info.description = adapter.description; 
				info.address = new byte[adapter.addressLength];
				Array.Copy( adapter.address, 0, info.address, 0, adapter.addressLength );  
				info.index = adapter.index;  
				info.type = adapter.type;  
				info.dhcpEnabled = adapter.dhcpEnabled == 1;  

				IPAddress ipaddr;
				IntPtr current;

				info.ipAddressList = new ArrayList();
				ipaddr = IPAddress.Parse( adapter.ipAddressList.ipAddress );
				info.ipAddressList.Add( ipaddr );
				current = adapter.ipAddressList.Next;
				while ( current != IntPtr.Zero )
				{
					_IP_ADDR_STRING addr = (_IP_ADDR_STRING)Marshal.PtrToStructure( current, typeof( _IP_ADDR_STRING ) );
					ipaddr = IPAddress.Parse( addr.ipAddress );
					info.ipAddressList.Add( ipaddr );
					current = addr.Next;
				}

				info.gatewayList = new ArrayList();
				ipaddr = IPAddress.Parse( adapter.gatewayList.ipAddress );
				info.gatewayList.Add( ipaddr );
				current = adapter.gatewayList.Next;
				while ( current != IntPtr.Zero )
				{
					_IP_ADDR_STRING addr = (_IP_ADDR_STRING)Marshal.PtrToStructure( current, typeof( _IP_ADDR_STRING ) );
					ipaddr = IPAddress.Parse( addr.ipAddress );
					info.gatewayList.Add( ipaddr );
					current = addr.Next;
				}

				info.dhcpServerList = new ArrayList();
				ipaddr = IPAddress.Parse( adapter.dhcpServer.ipAddress );
				info.dhcpServerList.Add( ipaddr );
				current = adapter.dhcpServer.Next;
				while ( current != IntPtr.Zero )
				{
					_IP_ADDR_STRING addr = (_IP_ADDR_STRING)Marshal.PtrToStructure( current, typeof( _IP_ADDR_STRING ) );
					ipaddr = IPAddress.Parse( addr.ipAddress );
					info.dhcpServerList.Add( ipaddr );
					current = addr.Next;
				}

				info.haveWins = adapter.haveWins == 1;  
				info.primaryWinsServer = IPAddress.Parse( adapter.primaryWinsServer.ipAddress );  
				info.secondaryWinsServer = IPAddress.Parse( adapter.secondaryWinsServer.ipAddress );  
				info.leaseObtained = new TimeSpan( adapter.leaseObtained );  
				info.leaseExpires = new TimeSpan( adapter.leaseExpires );

				infos.Add( info );
				pos = adapter.next;
			}

			Marshal.FreeHGlobal( lpBuffer );
			lpBuffer = IntPtr.Zero;

			return NO_ERROR;
		}


		private static TcpState ConvertTcpStateToEnum( int state )
		{
			TcpState e = (TcpState)Enum.Parse( typeof( TcpState ), state.ToString() );
			return e;
		}

		private static UInt16 ConvertTcpPort( UInt32 dwPort )
		{
			byte[] b = new Byte[2];
			// high weight byte
			b[0] = byte.Parse( (dwPort >> 8).ToString() );
			// low weight byte
			b[1] = byte.Parse( (dwPort & 0xFF).ToString() );

			return BitConverter.ToUInt16( b, 0 );
		}

		
		#region Hidden Exports and Structures

		[StructLayout(LayoutKind.Sequential)]
		private struct _IP_ADDR_STRING
		{
			public IntPtr Next;
			[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=16 ) ]
			public string ipAddress;
			[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=16 )]
			public string ipMask;
			public int context;
		}

		[StructLayout(LayoutKind.Sequential)]
		private struct _IP_ADAPTER_INFO 
		{
			public IntPtr next;
			public int comboIndex;
			[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=260 ) ]
			public string adapterName;  
			[ MarshalAs( UnmanagedType.ByValTStr, SizeConst=132 ) ]
			public string description;  
			public uint addressLength;  
			[ MarshalAs( UnmanagedType.ByValArray, ArraySubType=UnmanagedType.U1, SizeConst=8 ) ]
			public byte[] address;  
			public int index;  
			public uint type;  
			public uint dhcpEnabled;  
			public IntPtr currentIpAddress;  
			public _IP_ADDR_STRING ipAddressList;  
			public _IP_ADDR_STRING gatewayList;  
			public _IP_ADDR_STRING dhcpServer;  
			public int haveWins;  
			public _IP_ADDR_STRING primaryWinsServer;  
			public _IP_ADDR_STRING secondaryWinsServer;  
			public long leaseObtained;  
			public long leaseExpires;
		}

		[DllImport("iphlpapi.dll", EntryPoint="GetTcpTable", SetLastError=true)]
		private static extern int InternalGetTcpTable(byte[] pTcpTable, ref int pdwSize, bool bOrder);

		[DllImport("iphlpapi.dll", EntryPoint="GetUdpTable", SetLastError=true)]
		private static extern int InternalGetUdpTable(byte[] pUdpTable, ref int pdwSize, bool bOrder);

		[DllImport("iphlpapi.dll", EntryPoint="AllocateAndGetTcpExTableFromStack", SetLastError=true)]
		private extern static int InternalAllocateAndGetTcpExTableFromStack(ref IntPtr pTable, bool bOrder, IntPtr heap ,int zero,int flags );

		[DllImport("iphlpapi.dll", EntryPoint="AllocateAndGetUdpExTableFromStack", SetLastError=true)]
		private extern static int InternalAllocateAndGetUdpExTableFromStack(ref IntPtr pTable, bool bOrder, IntPtr heap, int zero,int flags );

		[DllImport( "kernel32", EntryPoint="GetProcessHeap", SetLastError=true)]
		private static extern IntPtr InternalGetProcessHeap(); 

		[DllImport("iphlpapi.dll", EntryPoint="GetNetworkParams", SetLastError=true)]
		private static extern int InternalGetNetworkParams(IntPtr pFixedInfo, ref int pdwSize );

		[DllImport("iphlpapi.dll", EntryPoint="GetAdaptersInfo", SetLastError=true)]
		private static extern int InternalGetAdaptersInfo(IntPtr pIpAdapterInfo, ref uint pulSize );

		#endregion
	}
}
